package cz.maestro.sokodroid;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.*;
import android.hardware.*;

import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

@SuppressWarnings("deprecation")
class GLView extends GLSurfaceView implements OnTouchListener, SensorListener {
	public GLRenderer renderer;
	private Sokodroid activity;

	private int touchMode = 0;
	private int oldX;
	private int oldY;
	private float sy = 0;

	GLView(Context context) {
		super(context);

		activity = (Sokodroid) context;
		renderer = new GLRenderer();
		setRenderer(renderer);

		setFocusable(true);
		setFocusableInTouchMode(true);
		requestFocus();

		setOnTouchListener(this);
	}

	public void onSensorChanged(int sensor, float[] values) {
		if (Math.abs(sy - values[1]) > 0.1) {
			float y = -(sy + values[1]) / 2f;
			if (Math.abs(y) > 70)
				y = Math.signum(y) * 70;

			final float yy = y;
			queueEvent(new Runnable() {
				public void run() {
					renderer.viewAngle(yy);
				}
			});
			sy = values[1];
		}
	}

	public void onAccuracyChanged(int sensor, int accuracy) {
	}

	public boolean onKeyDown(int key, KeyEvent event) {
		return activity.onKeyDown(key, event);
	}

	public boolean onTouch(View view, MotionEvent event) {
		int eventAction = event.getAction();
		final int x = Math.round(event.getX());
		final int y = Math.round(event.getY());
		switch (eventAction) {
		case MotionEvent.ACTION_DOWN:
			oldX = x;
			oldY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			if ((touchMode != 1 && oldY < 320 / 3) || touchMode == 2) {
				touchMode = 2;
				queueEvent(new Runnable() {
					public void run() {
						renderer.viewRotation((oldX - x) / 3.0f);
					}
				});
			} else {
				touchMode = 1;
				queueEvent(new Runnable() {
					public void run() {
						renderer.moveCamera(x - oldX, y - oldY);
					}
				});
			}
			oldX = x;
			oldY = y;
			break;
		case MotionEvent.ACTION_UP:
			touchMode = 0;
		}
		invalidate();
		return true;
	}
}

class GLRenderer implements GLSurfaceView.Renderer {
	public volatile Maze maze = null;

	private float zoom = -12.0f;

	private float angleX = 0.0f;
	private float angleZ = 0.0f;
	private float cameraX = 0.0f;
	private float cameraY = 0.0f;

	private int step = 0;
	private float steps[] = { 0.08f, 0.08f, 0.08f, 0.07f, 0.07f, 0.07f, 0.07f,
			0.07f, 0.07f, 0.06f, 0.06f, 0.06f, 0.06f, 0.06f, 0.05f, 0.05f, };
	private float movex = 0;
	private float movey = 0;

	GLRenderer() {
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		float lightAmbient[] = new float[] { 0.2f, 0.2f, 0.2f, 1 };
		float lightDiffuse[] = new float[] { 1, 1, 1, 1 };
		float lightPos[] = new float[] { 1, 1, 1, 1 };
		float matAmbient[] = new float[] { 1, 1, 1, 1 };
		float matDiffuse[] = new float[] { 1, 1, 1, 1 };

		gl.glEnable(GL10.GL_LIGHTING);
		gl.glEnable(GL10.GL_LIGHT0);
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);

		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, matAmbient, 0);
		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDiffuse, 0);

		gl.glEnable(GL10.GL_DEPTH_TEST);
		gl.glDepthFunc(GL10.GL_LEQUAL);

		gl.glEnable(GL10.GL_TEXTURE_2D);
		gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
		gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		float ratio = (float) width / height;
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		GLU.gluPerspective(gl, 45.0f, ratio, 1, 100f);
	}

	public void onDrawFrame(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();

		gl.glTranslatef(0, 0, zoom);
		gl.glScalef(1, -1, 1);

		gl.glRotatef(angleX, 1, 0, 0);
		gl.glRotatef(angleZ, 0, 0, 1);

		if (step != 0) {
			cameraX += movex * steps[steps.length - step];
			cameraY += movey * steps[steps.length - step];
			step--;
		}

		gl.glTranslatef(-cameraX, -cameraY, 0);
		if (maze != null)
			maze.draw(gl);
	}

	public void viewRotation(float z) {
		angleZ += z;
	}

	public void viewAngle(float x) {
		angleX = x;
	}

	public void moveCamera(int x, int y) {
		cameraX += x / 40.0f;
		cameraY += y / 40.0f;
	}

	public void setCamera(int x, int y, boolean now) {
		if (now) {
			cameraX = x + 0.5f;
			cameraY = y + 0.5f;
		} else if (x > cameraX + 4 || x < cameraX - 5 || y > cameraY + 2
				|| y < cameraY - 3) {
			step = steps.length;
			this.movex = (x - cameraX) + 0.5f;
			this.movey = (y - cameraY) + 0.5f;
		}
	}
}